package de.chimos.property.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javafx.beans.property.ReadOnlyProperty;

public class BeanUtil
{
	public static <T> Collection<PropertyWrapper<T>> getProperties(Object bean)
	{
		Map<String, PropertyWrapper<T>> properties = new HashMap<>();
		
		for(Field field : bean.getClass().getFields())
		{
			String name = field.getName();
			
			if(name.startsWith("_"))
			{
				name = name.substring(1);
			}
			
			properties.put(name, new PropertyWrapper<T>(bean, name, null, field));
		}
		
		for(Method method : bean.getClass().getMethods())
		{
			if(method.getParameterTypes().length != 0)
			{
				continue;
			}
			
			String name = method.getName();
			
			if(name.startsWith("get") == false || name.length() <= 3)
			{
				continue;
			}
			
			try
			{
				method.invoke(bean);
			}
			catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
			{
				continue;
			}
			
			String setterName = "set"+name.substring(3);
			name = name.substring(3, 4).toLowerCase()+name.substring(4);
			
			Method setterMethod = null;
			
			try {
				setterMethod = bean.getClass().getMethod(setterName, method.getReturnType());
			} catch (NoSuchMethodException | SecurityException e) { }
			
			properties.put(name, new PropertyWrapper<T>(bean, name, null, method, setterMethod));
		}
		
		for(Method method : bean.getClass().getMethods())
		{
			if(method.getName().endsWith("Property") == false)
			{
				continue;
			}
			
			Object returnValue;
			
			try {
				returnValue = method.invoke(bean);
			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				throw new IllegalStateException();
			}
			
			de.chimos.property.annotation.Property propertyAnnotation = method.getAnnotation(de.chimos.property.annotation.Property.class);
			String humanReadableName = (propertyAnnotation != null && propertyAnnotation.humanReadableName() != null && propertyAnnotation.humanReadableName().length() > 0) ? propertyAnnotation.humanReadableName() : null;
			
			if(returnValue instanceof ReadOnlyProperty<?>)
			{
				@SuppressWarnings("unchecked")
				ReadOnlyProperty<T> property = (ReadOnlyProperty<T>)returnValue;
				
				properties.put(property.getName(), new PropertyWrapper<T>(property, humanReadableName));
			}
		}
		
		return properties.values();
	}
}
